﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using Newtonsoft.Json;
using SmartQuant;
using QuantBox;
using Serilog;

namespace OpenQuant
{
    public class Cfg
    {
        public static Dictionary<Tuple<string, string>, Tuple<bool, int, double>> SFCfg = new Dictionary<Tuple<string, string>, Tuple<bool, int, double>>();

        public static DateTime FrDate;      //For Backtest
        public static DateTime OCDate;      //Before Which You CANNOT Open Or Close Positions
        public static DateTime ToDate;      //For Backtest
        public static DateTime KillDT;      //For Futures Rolling At Realtime/PostTrade Mode
        public static int PosChgHour = 17;  //For Realtime
        public static int PosChgMin = 0;   //For Realtime

        public static string InstrDir;
        public static string LogDir = @"c:\work\data\temp\";
        public static string LogName = "test";
        public static string PosDir;

        public static bool PrintEnabled = true;
        public static bool FixQtyEnabled = true;  //Never Change
        public static bool ManualPosEnabled = false;

        public static Dictionary<string, string> Sym = new Dictionary<string, string>();
        public static Dictionary<string, int> LimFutPosOpen0 = new Dictionary<string, int>();
        public static Dictionary<string, int> LimFutPosOpen = new Dictionary<string, int>();
        public static Dictionary<string, DateTime> PreDate = new Dictionary<string, DateTime>();
        public static Dictionary<Tuple<string, string>, bool> OverNightEnabled = new Dictionary<Tuple<string, string>, bool>();
        public static Dictionary<string, bool> LockFutPosEnabled = new Dictionary<string, bool>();

        public static Dictionary<Tuple<string, string>, string[]> StraFutPar = new Dictionary<Tuple<string, string>, string[]>();

        public static StrategyMode RTMode;  //Live Or Paper
        public static int LoadFutDays;
        public static string ServerData;
        public static string ServerExec;
        public static string UserId;
        public static string Password;
    }

    public class Cmn
    {
        public static bool BacktestBeforeLive = false;                //Never Change
        public static DateTime NowDate = new DateTime(1900, 01, 01);  //Never Change
        public static readonly Serilog.ILogger Logger;

        static Cmn()
        {
            Logger = QBHelper.GetFileLogger($"{Cfg.LogDir}{Cfg.LogName}_.log", rollingInterval: RollingInterval.Day, flushToDiskInterval: 1);
        }

        public static void WriteLog(string str, bool doEnter = true)
        {
            //if (doEnter) Console.WriteLine(str); else Console.Write(str);
            //DirectoryInfo DI = new DirectoryInfo(Cfg.LogDir);
            //if (!DI.Exists) DI.Create();
            //StreamWriter File = new StreamWriter(Cfg.LogDir + DateTime.Now.Date.ToString("yyyyMMdd") + "_" + Cfg.LogName, true);
            //if (doEnter) File.WriteLine(str); else File.Write(str);
            //File.Flush();
            //File.Close();
            Logger.Debug(str);
        }
        public static object objLock = new object();
        public static void MySendOrder(Strategy stra, Instrument instrument, OrderSide orderSide, int qty, double price, string text, bool lockFutPosEnabled = true, object info = null)
        {
            if (BacktestBeforeLive) return;
            if (instrument.Type != InstrumentType.Future) return;

            if (Cfg.ManualPosEnabled && stra.Mode != StrategyMode.Backtest)
            {
                //Persistence.LoadAccountPosition();
            }

            if (lockFutPosEnabled)
            {
                lock (objLock)
                {
                    var instrPos = stra.GetPosition(instrument)!;
                    //MyInstrumentPosition instrPos = MyAccountPosition.GetInstrumentPosition(instrument.Symbol);
                    var availableYesterdayLong = (int)(instrPos.LongAvailable - instrPos.LongToday);
                    var availableYesterdayShort = (int)(instrPos.ShortAvailable - instrPos.ShortToday);
                    if (orderSide == OrderSide.Buy)
                    {
                        if (availableYesterdayShort == 0 || (instrPos.ShortToday > 0 && instrument.Exchange == "CFFEX"))
                        {
                            //NO yesterday short position, open today long position
                            qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                            Order order = stra.BuyLimitOrder(instrument, qty, price, text).Open();
                            if (info != null) order.Fields[1] = info;
                            order.SendWithSelfTradeCheck();
                            if (Cfg.PrintEnabled)
                            {
                                WriteLog("01-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                WriteLog("");
                            }
                        }
                        else if (availableYesterdayShort > 0 && availableYesterdayShort >= qty)
                        {
                            //Enough yesterday short position, cover part or all of it
                            Order order = stra.BuyLimitOrder(instrument, qty, price, text).Close();
                            if (info != null) order.Fields[1] = info;
                            order.SendWithSelfTradeCheck();

                            //instrPos.FrozenYesterdayShort += qty; 
                            //if (stra.Mode != StrategyMode.Backtest) 
                            //    Persistence.SaveAccountPosition();
                            if (Cfg.PrintEnabled)
                            {
                                WriteLog("02-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                WriteLog("");
                            }
                        }
                        else if (availableYesterdayShort > 0 && availableYesterdayShort < qty)
                        {
                            //NOT enough yesterday short position, cover all of it first
                            Order order1 = stra.BuyLimitOrder(instrument, availableYesterdayShort, price, text).Close();
                            if (info != null) order1.Fields[1] = info;
                            order1.SendWithSelfTradeCheck();
                            //instrPos.FrozenYesterdayShort += availableYesterdayShort; 
                            if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                            if (Cfg.PrintEnabled)
                            {
                                WriteLog("03-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + availableYesterdayShort + "    " + price + "    " + text);
                                WriteLog("");
                            }

                            //Open remain as today long position
                            qty = qty - availableYesterdayShort;
                            qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                            Order order2 = stra.BuyLimitOrder(instrument, qty, price, text);
                            if (info != null) order2.Fields[1] = info;
                            order2.Open().SendWithSelfTradeCheck();
                            if (Cfg.PrintEnabled)
                            {
                                WriteLog("04-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                WriteLog("");
                            }
                        }
                    }
                    else if (orderSide == OrderSide.Sell)
                    {
                        if (availableYesterdayLong == 0 || (instrPos.LongToday > 0 && instrument.Exchange == "CFFEX"))
                        {
                            //NO yesterday long position, open today short position
                            qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                            Order order = stra.SellLimitOrder(instrument, qty, price, text);
                            if (info != null) order.Fields[1] = info;
                            order.Open().SendWithSelfTradeCheck();
                            if (Cfg.PrintEnabled)
                            {
                                WriteLog("05-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                WriteLog("");
                            }
                        }
                        else if (availableYesterdayLong > 0 && availableYesterdayLong >= qty)
                        {
                            //Enough yesterday long postion, cover part or all of it
                            Order order = stra.SellLimitOrder(instrument, qty, price, text);
                            if (info != null) order.Fields[1] = info;
                            order.Close().SendWithSelfTradeCheck();
                            //instrPos.FrozenYesterdayLong += qty; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                            if (Cfg.PrintEnabled)
                            {
                                WriteLog("06-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                WriteLog("");
                            }
                        }
                        else if (availableYesterdayLong > 0 && availableYesterdayLong < qty)
                        {
                            //NOT enough yesterday long position, cover all of it first
                            Order order1 = stra.SellLimitOrder(instrument, availableYesterdayLong, price, text);
                            if (info != null) order1.Fields[1] = info;
                            order1.Close().SendWithSelfTradeCheck();
                            //instrPos.FrozenYesterdayLong += availableYesterdayLong; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                            if (Cfg.PrintEnabled)
                            {
                                WriteLog("07-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + availableYesterdayLong + "    " + price + "    " + text);
                                WriteLog("");
                            }

                            //Open remain as today short position
                            qty = qty - availableYesterdayLong;
                            qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                            Order order2 = stra.SellLimitOrder(instrument, qty, price, text);
                            if (info != null) order2.Fields[1] = info;
                            order2.Open().SendWithSelfTradeCheck();
                            if (Cfg.PrintEnabled)
                            {
                                WriteLog("08-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                WriteLog("");
                            }
                        }
                    }
                }
            }
            else
            {
                lock (objLock)
                {
                    var instrPos = stra.GetPosition(instrument)!;
                    //MyInstrumentPosition instrPos = MyAccountPosition.GetInstrumentPosition(instrument.Symbol);
                    var avaLong = (int)instrPos.LongAvailable;
                    var avaShort = (int)instrPos.ShortAvailable;
                    //var avaLong = instrPos.YesterdayLong + instrPos.TodayLong - instrPos.FrozenYesterdayLong - instrPos.FrozenTodayLong;
                    //var avaShort = instrPos.YesterdayShort + instrPos.TodayShort - instrPos.FrozenYesterdayShort - instrPos.FrozenTodayShort;
                    if (instrument.Exchange != "SHFE" && instrument.Exchange != "INE")
                    {
                        if (orderSide == OrderSide.Buy)
                        {
                            if (avaShort > 0 && avaShort >= qty)
                            {
                                //Enough short position, cover part or all of it
                                Order order = stra.BuyLimitOrder(instrument, qty, price, text);
                                if (info != null) order.Fields[1] = info;
                                order.Close().SendWithSelfTradeCheck();
                                //instrPos.FrozenYesterdayShort += qty; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("09-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                            else if (avaShort > 0 && avaShort < qty)
                            {
                                //NOT enough short position, cover all of it first
                                Order order1 = stra.BuyLimitOrder(instrument, avaShort, price, text);
                                if (info != null) order1.Fields[1] = info;
                                order1.Close().SendWithSelfTradeCheck();
                                //instrPos.FrozenYesterdayShort += avaShort; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("10-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + avaShort + "    " + price + "    " + text);
                                    WriteLog("");
                                }

                                //Open remain as long position
                                qty = qty - avaShort;
                                qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                                Order order2 = stra.BuyLimitOrder(instrument, qty, price, text);
                                if (info != null) order2.Fields[1] = info;
                                order2.Open().SendWithSelfTradeCheck();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("11-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                            else if (avaShort == 0)
                            {
                                //NO short position, open long position
                                qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                                Order order = stra.BuyLimitOrder(instrument, qty, price, text);
                                if (info != null) order.Fields[1] = info;
                                order.Open().SendWithSelfTradeCheck();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("12-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                        }
                        else if (orderSide == OrderSide.Sell)
                        {
                            if (avaLong > 0 && avaLong >= qty)
                            {
                                //Enough long postion, cover part or all of it
                                Order order = stra.SellLimitOrder(instrument, qty, price, text);
                                if (info != null) order.Fields[1] = info;
                                order.Close().SendWithSelfTradeCheck();
                                //instrPos.FrozenYesterdayLong += qty; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("13-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                            else if (avaLong > 0 && avaLong < qty)
                            {
                                //NOT enough long position, cover all of it first
                                Order order1 = stra.SellLimitOrder(instrument, avaLong, price, text);
                                if (info != null) order1.Fields[1] = info;
                                order1.Close().SendWithSelfTradeCheck();
                                //instrPos.FrozenYesterdayLong += avaLong; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("14-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + avaLong + "    " + price + "    " + text);
                                    WriteLog("");
                                }

                                //Open remain as short position
                                qty = qty - avaLong;
                                qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                                Order order2 = stra.SellLimitOrder(instrument, qty, price, text);
                                if (info != null) order2.Fields[1] = info;
                                order2.Open().SendWithSelfTradeCheck();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("15-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                            else if (avaLong == 0)
                            {
                                //NO yesterday long position, open today short position
                                qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                                Order order = stra.SellLimitOrder(instrument, qty, price, text);
                                if (info != null) order.Fields[1] = info;
                                order.Open().SendWithSelfTradeCheck();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("16-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                        }
                    }
                    else
                    {
                        var avaTodayLong = instrPos.LongToday - instrPos.LongFrozenToday;
                        var avaTodayShort = instrPos.ShortToday - instrPos.ShortFrozenToday;
                        var avaYesterdayLong = instrPos.LongYesterday - instrPos.LongFrozenYesterday;
                        var avaYesterdayShort = instrPos.ShortYesterday - instrPos.ShortFrozenYesterday;
                        //var avaTodayLong = instrPos.TodayLong - instrPos.FrozenTodayLong;
                        //var avaTodayShort = instrPos.TodayShort - instrPos.FrozenTodayShort;
                        //var avaYesterdayLong = instrPos.YesterdayLong - instrPos.FrozenYesterdayLong;
                        //var avaYesterdayShort = instrPos.YesterdayShort - instrPos.FrozenYesterdayShort;
                        if (orderSide == OrderSide.Buy)
                        {
                            if (avaShort > 0 && avaShort >= qty)
                            {
                                //Enough short position, cover part or all of it
                                var qty1 = Math.Min(avaYesterdayShort, qty);
                                var qty2 = qty - qty1;
                                if (qty1 > 0)
                                {
                                    Order order1 = stra.BuyLimitOrder(instrument, qty1, price, text);
                                    if (info != null) order1.Fields[1] = info;
                                    order1.Close().SendWithSelfTradeCheck();
                                    //instrPos.FrozenYesterdayShort += qty1; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                    if (Cfg.PrintEnabled)
                                    {
                                        WriteLog("17-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty1 + "    " + price + "    " + text);
                                        WriteLog("");
                                    }
                                }
                                if (qty2 > 0)
                                {
                                    Order order2 = stra.BuyLimitOrder(instrument, qty2, price, text);
                                    if (info != null) order2.Fields[1] = info;
                                    order2.CloseToday().SendWithSelfTradeCheck();
                                    //instrPos.FrozenTodayShort += qty2; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                    if (Cfg.PrintEnabled)
                                    {
                                        WriteLog("18-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty2 + "    " + price + "    " + text);
                                        WriteLog("");
                                    }
                                }
                            }
                            else if (avaShort > 0 && avaShort < qty)
                            {
                                //NOT enough short position, cover all of it first
                                if (avaYesterdayShort > 0)
                                {
                                    Order order1 = stra.BuyLimitOrder(instrument, avaYesterdayShort, price, text);
                                    if (info != null) order1.Fields[1] = info;
                                    order1.Close().SendWithSelfTradeCheck();
                                    //instrPos.FrozenYesterdayShort += avaYesterdayShort; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                    if (Cfg.PrintEnabled)
                                    {
                                        WriteLog("19-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + avaYesterdayShort + "    " + price + "    " + text);
                                        WriteLog("");
                                    }
                                }
                                if (avaTodayShort > 0)
                                {
                                    Order order2 = stra.BuyLimitOrder(instrument, avaTodayShort, price, text);
                                    if (info != null) order2.Fields[1] = info;
                                    order2.CloseToday().SendWithSelfTradeCheck();
                                    //instrPos.FrozenTodayShort += avaTodayShort; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                    if (Cfg.PrintEnabled)
                                    {
                                        WriteLog("20-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + avaTodayShort + "    " + price + "    " + text);
                                        WriteLog("");
                                    }
                                }

                                //Open remain as long position
                                qty = qty - avaShort;
                                qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                                Order order3 = stra.BuyLimitOrder(instrument, qty, price, text);
                                if (info != null) order3.Fields[1] = info;
                                order3.Open().SendWithSelfTradeCheck();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("21-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                            else if (avaShort == 0)
                            {
                                //NO short position, open long position
                                qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                                Order order = stra.BuyLimitOrder(instrument, qty, price, text);
                                if (info != null) order.Fields[1] = info;
                                order.Open().SendWithSelfTradeCheck();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("22-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                        }
                        else if (orderSide == OrderSide.Sell)
                        {
                            if (avaLong > 0 && avaLong >= qty)
                            {
                                //Enough long postion, cover part or all of it
                                var qty1 = Math.Min(avaYesterdayLong, qty);
                                var qty2 = qty - qty1;
                                if (qty1 > 0)
                                {
                                    Order order1 = stra.SellLimitOrder(instrument, qty1, price, text);
                                    if (info != null) order1.Fields[1] = info;
                                    order1.Close().SendWithSelfTradeCheck();
                                    //instrPos.FrozenYesterdayLong += qty1; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                    if (Cfg.PrintEnabled)
                                    {
                                        WriteLog("23-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty1 + "    " + price + "    " + text);
                                        WriteLog("");
                                    }
                                }
                                if (qty2 > 0)
                                {
                                    Order order2 = stra.SellLimitOrder(instrument, qty2, price, text);
                                    if (info != null) order2.Fields[1] = info;
                                    order2.CloseToday().SendWithSelfTradeCheck();
                                    //instrPos.FrozenTodayLong += qty2; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                    if (Cfg.PrintEnabled)
                                    {
                                        WriteLog("24-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty2 + "    " + price + "    " + text);
                                        WriteLog("");
                                    }
                                }
                            }
                            else if (avaLong > 0 && avaLong < qty)
                            {
                                //NOT enough long position, cover all of it first
                                if (avaYesterdayLong > 0)
                                {
                                    Order order1 = stra.SellLimitOrder(instrument, avaYesterdayLong, price, text);
                                    if (info != null) order1.Fields[1] = info;
                                    order1.Close().SendWithSelfTradeCheck();
                                    //instrPos.FrozenYesterdayLong += avaYesterdayLong; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                    if (Cfg.PrintEnabled)
                                    {
                                        WriteLog("25-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + avaYesterdayLong + "    " + price + "    " + text);
                                        WriteLog("");
                                    }
                                }
                                if (avaTodayLong > 0)
                                {
                                    Order order2 = stra.SellLimitOrder(instrument, avaTodayLong, price, text);
                                    if (info != null) order2.Fields[1] = info;
                                    order2.CloseToday().SendWithSelfTradeCheck();
                                    //instrPos.FrozenTodayLong += avaTodayLong; if (stra.Mode != StrategyMode.Backtest) Persistence.SaveAccountPosition();
                                    if (Cfg.PrintEnabled)
                                    {
                                        WriteLog("26-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + avaTodayLong + "    " + price + "    " + text);
                                        WriteLog("");
                                    }
                                }

                                //Open remain as short position
                                qty = qty - avaLong;
                                qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                                Order order3 = stra.SellLimitOrder(instrument, qty, price, text);
                                if (info != null) order3.Fields[1] = info;
                                order3.Open().SendWithSelfTradeCheck();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("27-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                            else if (avaLong == 0)
                            {
                                //NO yesterday long position, open today short position
                                qty = Cmn.LimitFutPosOpen(instrument.Symbol, qty, text); if (qty == 0) return;
                                Order order = stra.SellLimitOrder(instrument, qty, price, text);
                                if (info != null) order.Fields[1] = info;
                                order.Open().SendWithSelfTradeCheck();
                                if (Cfg.PrintEnabled)
                                {
                                    WriteLog("28-$$$$$$$$$$$$$$$$$$ " + instrument.Trade.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "    " + instrument.Symbol + "    " + orderSide + "    " + qty + "    " + price + "    " + text);
                                    WriteLog("");
                                }
                            }
                        }
                    }
                }
            }
        }
        public static bool OnMyPosition(int preAmount, int nowAmount, string text)
        {
            if (text == "Opened")
            {
                if (preAmount == 0)
                {
                    if (nowAmount != 0) return true;
                }
                else if (preAmount > 0)
                {
                    if (nowAmount > preAmount) return true;
                }
                else
                {
                    if (nowAmount < preAmount) return true;
                }
            }
            else if (text == "RoundOpened")
            {
                if (preAmount == 0)
                {
                    if (nowAmount != 0) return true;
                }
            }
            else if (text == "Closed")
            {
                if (preAmount > 0)
                {
                    if (nowAmount < preAmount && nowAmount >= 0) return true;
                }
                else if (preAmount < 0)
                {
                    if (nowAmount > preAmount && nowAmount <= 0) return true;
                }
            }
            else if (text == "RoundClosed")
            {
                if (preAmount != 0)
                {
                    if (nowAmount == 0) return true;
                }
            }
            return false;
        }
        public static string[] GetFut(DateTime dt, string symbol)
        {
            string futPath = GetFutPath(symbol);
            StreamReader Reader = new StreamReader(futPath);
            string line = Reader.ReadLine();
            string[] items = line.Split(',');
            var trdDate = DateTime.Parse(items[0]);
            if (trdDate > dt) return null;
            string sym = items[1];
            string preSym = items[1];
            string endSign = items[2];
            string preEndSign = items[2];
            string[] res = new string[4] { sym, preSym, endSign, preEndSign };
            line = Reader.ReadLine();
            while (line != null)
            {
                items = line.Split(',');
                trdDate = DateTime.Parse(items[0]);
                preSym = sym;
                preEndSign = endSign;
                if (trdDate <= dt)
                {
                    sym = items[1];
                    endSign = items[2];
                    res[0] = sym;
                    res[1] = preSym;
                    res[2] = endSign;
                    res[3] = preEndSign;
                }
                line = Reader.ReadLine();
            }
            return res;
        }
        public static int LimitFutPosOpen(string sym, int qty, string text)
        {
            if (Cfg.LimFutPosOpen[sym] == 0)
            {
                if (Cfg.PrintEnabled) WriteLog("!!!!!!!!!!!!!!!!!! Warning (" + text + "): LimFutPosOpen = 0, NO open allowed!");
                return 0;
            }
            if (Cfg.PrintEnabled && qty > Cfg.LimFutPosOpen[sym])
            {
                WriteLog("!!!!!!!!!!!!!!!!!! Warning (" + text + "): Only part of open qty (" + Cfg.LimFutPosOpen[sym] + ") allowed!");
            }
            qty = Math.Min(qty, Cfg.LimFutPosOpen[sym]);
            if (Cfg.PrintEnabled) WriteLog("~~~~~~~~~~~~~~~~~~ LimFutPosOpen (" + text + "): " + Cfg.LimFutPosOpen[sym] + " --> ", false);
            Cfg.LimFutPosOpen[sym] = Math.Max(0, Cfg.LimFutPosOpen[sym] - qty);
            if (Cfg.PrintEnabled) WriteLog(Cfg.LimFutPosOpen[sym].ToString());
            return qty;
        }
        public static void ResetLimFutPosOpen(Strategy stra, Instrument instrument, DateTime dt)
        {
            if (instrument.Type != InstrumentType.Future) return;
            if (false == Cfg.PreDate.Keys.Contains(instrument.Symbol)) return;
            var nowDate = dt.Date;
            if (dt.TimeOfDay >= new TimeSpan(20, 55, 00) && dt.TimeOfDay <= new TimeSpan(23, 59, 59))
            {
                if (dt.DayOfWeek.ToString() != "Friday") nowDate = nowDate.AddDays(1); else nowDate = nowDate.AddDays(3);
            }
            if (nowDate != Cfg.PreDate[instrument.Symbol])
            {
                Cfg.LimFutPosOpen[instrument.Symbol] = Cfg.LimFutPosOpen0[instrument.Symbol];
                if (stra.Mode == StrategyMode.Backtest && false == BacktestBeforeLive)
                {
                    foreach (var item in MyAccountPosition.AccountPositions)
                    {
                        MyInstrumentPosition instrPos = item.Value;
                        instrPos.ChangeDay();
                    }
                    Persistence.SaveAccountPosition();
                }
            }
            Cfg.PreDate[instrument.Symbol] = nowDate;
        }
        public static Tuple<string, string> t1(string straName, string futName)
        {
            return (new Tuple<string, string>(straName, futName));
        }
        public static Tuple<bool, int, double> t2(bool straEnabled, int orderQty, double initMoney)
        {
            return (new Tuple<bool, int, double>(straEnabled, orderQty, initMoney));
        }
        public static string OmitDigit(string str)
        {
            return (Regex.Replace(str, "\\d", ""));
        }
        public static string GetFutPath(string symbol)
        {
            return (Cfg.InstrDir + OmitDigit(symbol) + ".all");
        }
        public static List<Instrument> GetAllInstrument(Framework framework, IInstrumentProvider provider)
        {
            var wait = new AutoResetEvent(false);
            var insts = new List<Instrument>();
            framework.EventManager.Dispatcher.InstrumentDefinition += (s, e) => { insts.AddRange(e.Definition.Instruments); };
            framework.EventManager.Dispatcher.InstrumentDefinitionEnd += (s, e) => { wait.Set(); };
            var requestId = Guid.NewGuid().ToString();
            InstrumentDefinitionRequest request = new InstrumentDefinitionRequest();
            request.FilterType = InstrumentType.Future;
            request.Id = requestId;
            provider.Send(request);
            wait.WaitOne();
            return insts;
        }
        public static Instrument GetOneInstrument(List<Instrument> insts, string name)
        {
            foreach (Instrument i in insts)
            {
                if (i.Symbol == name) return i;
            }
            return null;
        }
        public static void LoadHistoricalData(Instrument instrument, DataManager DataManager, int days)
        {
            DataManager.DeleteDataSeries(instrument, DataObjectType.Ask);
            DataManager.DeleteDataSeries(instrument, DataObjectType.Bid);
            DataManager.DeleteDataSeries(instrument, DataObjectType.Trade);
            DataManager.DeleteDataSeries(instrument, DataObjectType.Bar);
            var historicalStartTime = DateTime.Today.AddDays(-days);
            var (tickAsk, tickBid, tickTrade) = DataManager.DownloadTicks(instrument, historicalStartTime, DateTime.Today);
            DataManager.Save(instrument, tickAsk);
            DataManager.Save(instrument, tickBid);
            DataManager.Save(instrument, tickTrade);
        }
    }

    public class MyCommissionProvider : ICommissionProvider
    {
        private CommissionType type;
        private double commission;
        private double minCommission;
        private System.Collections.Hashtable ht;
        public MyCommissionProvider()
        {
            ht = new System.Collections.Hashtable();
        }
        ~MyCommissionProvider()
        {
            ht.Clear();
            ht = null;
        }
        public double Commission
        {
            get { return commission; }
            set { commission = value; }
        }
        public CommissionType Type
        {
            get { return type; }
            set { type = value; }
        }
        public double MinCommission
        {
            get { return minCommission; }
            set { minCommission = value; }
        }
        public virtual double GetCommission(ExecutionReport report)
        {
            double num;
            string symbol = report.Instrument.Symbol;
            Type = MyCommissionType(symbol);
            Commission = MyCommission(symbol);
            switch (type)
            {
                case CommissionType.PerShare:
                    num = commission * report.LastQty;
                    break;
                case CommissionType.Percent:
                    num = commission * report.LastQty * report.LastPx * (report.Instrument.Type == InstrumentType.Stock ? 1 : report.Instrument.Factor);
                    break;
                case CommissionType.Absolute:
                    num = commission;
                    break;
                default:
                    throw new NotSupportedException("!!!!!!!!!!!!!!!!!! Warning: Unknown commission type [" + type + "]!");
            }
            if (num < minCommission)
            {
                return minCommission;
            }
            return num;
        }
        static MyCommissionProvider()
        {
        }
        public static CommissionType MyCommissionType(string symbol)
        {
            CommissionType myType;
            switch (symbol.ToUpper().Substring(0, (symbol[1] < 65) ? 1 : 2))  // Updated According To 20200717
            {
                //CFFEX
                case "IC":
                    myType = CommissionType.Percent;
                    break;
                case "IF":
                    myType = CommissionType.Percent;
                    break;
                case "IH":
                    myType = CommissionType.Percent;
                    break;
                case "T":
                    myType = CommissionType.PerShare;
                    break;
                case "TF":
                    myType = CommissionType.PerShare;
                    break;
                case "TS":
                    myType = CommissionType.PerShare;
                    break;
                //CZCE
                case "AP":
                    myType = CommissionType.PerShare;
                    break;
                case "CF":
                    myType = CommissionType.PerShare;
                    break;
                case "CJ":
                    myType = CommissionType.PerShare;
                    break;
                case "CY":
                    myType = CommissionType.PerShare;
                    break;
                case "FG":
                    myType = CommissionType.PerShare;
                    break;
                case "JR":
                    myType = CommissionType.PerShare;
                    break;
                case "LR":
                    myType = CommissionType.PerShare;
                    break;
                case "MA":
                    myType = CommissionType.PerShare;
                    break;
                case "OI":
                    myType = CommissionType.PerShare;
                    break;
                case "PM":
                    myType = CommissionType.PerShare;
                    break;
                case "RI":
                    myType = CommissionType.PerShare;
                    break;
                case "RM":
                    myType = CommissionType.PerShare;
                    break;
                case "RS":
                    myType = CommissionType.PerShare;
                    break;
                case "SA":
                    myType = CommissionType.PerShare;
                    break;
                case "SF":
                    myType = CommissionType.PerShare;
                    break;
                case "SM":
                    myType = CommissionType.PerShare;
                    break;
                case "SR":
                    myType = CommissionType.PerShare;
                    break;
                case "TA":
                    myType = CommissionType.PerShare;
                    break;
                case "UR":
                    myType = CommissionType.PerShare;
                    break;
                case "WH":
                    myType = CommissionType.PerShare;
                    break;
                case "ZC":
                    myType = CommissionType.PerShare;
                    break;
                //DCE
                case "A":
                    myType = CommissionType.PerShare;
                    break;
                case "B":
                    myType = CommissionType.PerShare;
                    break;
                case "BB":
                    myType = CommissionType.Percent;
                    break;
                case "C":
                    myType = CommissionType.PerShare;
                    break;
                case "CS":
                    myType = CommissionType.PerShare;
                    break;
                case "EB":
                    myType = CommissionType.PerShare;
                    break;
                case "EG":
                    myType = CommissionType.PerShare;
                    break;
                case "FB":
                    myType = CommissionType.Percent;
                    break;
                case "I":
                    myType = CommissionType.Percent;
                    break;
                case "J":
                    myType = CommissionType.Percent;
                    break;
                case "JD":
                    myType = CommissionType.Percent;
                    break;
                case "JM":
                    myType = CommissionType.Percent;
                    break;
                case "L":
                    myType = CommissionType.PerShare;
                    break;
                case "M":
                    myType = CommissionType.PerShare;
                    break;
                case "P":
                    myType = CommissionType.PerShare;
                    break;
                case "PG":
                    myType = CommissionType.PerShare;
                    break;
                case "PP":
                    myType = CommissionType.Percent;
                    break;
                case "RR":
                    myType = CommissionType.PerShare;
                    break;
                case "V":
                    myType = CommissionType.PerShare;
                    break;
                case "Y":
                    myType = CommissionType.PerShare;
                    break;
                //INE
                case "LU":
                    myType = CommissionType.PerShare;
                    break;
                case "NR":
                    myType = CommissionType.PerShare;
                    break;
                case "SC":
                    myType = CommissionType.PerShare;
                    break;
                //SHFE
                case "AG":
                    myType = CommissionType.Percent;
                    break;
                case "AL":
                    myType = CommissionType.PerShare;
                    break;
                case "AU":
                    myType = CommissionType.PerShare;
                    break;
                case "BU":
                    myType = CommissionType.Percent;
                    break;
                case "CU":
                    myType = CommissionType.Percent;
                    break;
                case "FU":
                    myType = CommissionType.Percent;
                    break;
                case "HC":
                    myType = CommissionType.Percent;
                    break;
                case "NI":
                    myType = CommissionType.PerShare;
                    break;
                case "PB":
                    myType = CommissionType.Percent;
                    break;
                case "RB":
                    myType = CommissionType.Percent;
                    break;
                case "RU":
                    myType = CommissionType.PerShare;
                    break;
                case "SN":
                    myType = CommissionType.PerShare;
                    break;
                case "SP":
                    myType = CommissionType.Percent;
                    break;
                case "SS":
                    myType = CommissionType.PerShare;
                    break;
                case "WR":
                    myType = CommissionType.Percent;
                    break;
                case "ZN":
                    myType = CommissionType.PerShare;
                    break;
                //Stock
                case "0":
                case "3":
                case "6":
                    myType = CommissionType.Percent;
                    break;
                default:
                    myType = CommissionType.Percent;
                    break;
            }
            return myType;
        }
        public static double MyCommission(string symbol)
        {
            double myCommission = 0.0;
            switch (symbol.ToUpper().Substring(0, (symbol[1] < 65) ? 1 : 2))  // Updated According To 20200717
            {
                //CFFEX
                case "IC":
                    myCommission = 0.00003;
                    break;
                case "IH":
                    myCommission = 0.00003;
                    break;
                case "IF":
                    myCommission = 0.00003;
                    break;
                case "T":
                    myCommission = 1.5;
                    break;
                case "TF":
                    myCommission = 1.5;
                    break;
                case "TS":
                    myCommission = 1.5;
                    break;
                //CZCE
                case "AP":
                    myCommission = 5.0;
                    break;
                case "CF":
                    myCommission = 2.15;
                    break;
                case "CJ":
                    myCommission = 1.5;
                    break;
                case "CY":
                    myCommission = 2.0;
                    break;
                case "FG":
                    myCommission = 3.0;
                    break;
                case "JR":
                    myCommission = 3.0;
                    break;
                case "LR":
                    myCommission = 3.0;
                    break;
                case "MA":
                    myCommission = 2.0;
                    break;
                case "OI":
                    myCommission = 1.0;
                    break;
                case "PM":
                    myCommission = 5.0;
                    break;
                case "RI":
                    myCommission = 2.5;
                    break;
                case "RM":
                    myCommission = 0.75;
                    break;
                case "RS":
                    myCommission = 2.0;
                    break;
                case "SA":
                    myCommission = 1.75;
                    break;
                case "SF":
                    myCommission = 0.75;
                    break;
                case "SM":
                    myCommission = 1.5;
                    break;
                case "SR":
                    myCommission = 1.5;
                    break;
                case "TA":
                    myCommission = 1.5;
                    break;
                case "UR":
                    myCommission = 5.0;
                    break;
                case "WH":
                    myCommission = 5.0;
                    break;
                case "ZC":
                    myCommission = 1.5;
                    break;
                //DCE
                case "A":
                    myCommission = 2.0;
                    break;
                case "B":
                    myCommission = 1.0;
                    break;
                case "BB":
                    myCommission = 0.000075;
                    break;
                case "C":
                    myCommission = 0.9;
                    break;
                case "CS":
                    myCommission = 1.125;
                    break;
                case "EB":
                    myCommission = 3.0;
                    break;
                case "EG":
                    myCommission = 2.25;
                    break;
                case "FB":
                    myCommission = 0.0001;
                    break;
                case "I":
                    myCommission = 0.0001;
                    break;
                case "J":
                    myCommission = 0.00006;
                    break;
                case "JD":
                    myCommission = 0.00015;
                    break;
                case "JM":
                    myCommission = 0.00006;
                    break;
                case "L":
                    myCommission = 1.5;
                    break;
                case "M":
                    myCommission = 1.125;
                    break;
                case "P":
                    myCommission = 2.5;
                    break;
                case "PG":
                    myCommission = 6.0;
                    break;
                case "PP":
                    myCommission = 0.000045;
                    break;
                case "RR":
                    myCommission = 0.75;
                    break;
                case "V":
                    myCommission = 1.5;
                    break;
                case "Y":
                    myCommission = 1.875;
                    break;
                //INE
                case "LU":
                    myCommission = 2.0;
                    break;
                case "NR":
                    myCommission = 1.5;
                    break;
                case "SC":
                    myCommission = 10.0;
                    break;
                //SHFE
                case "AG":
                    myCommission = 0.00005;
                    break;
                case "AL":
                    myCommission = 1.5;
                    break;
                case "AU":
                    myCommission = 5.0;
                    break;
                case "BU":
                    myCommission = 0.0001;
                    break;
                case "CU":
                    myCommission = 0.000025;
                    break;
                case "FU":
                    myCommission = 0.00005;
                    break;
                case "HC":
                    myCommission = 0.0001;
                    break;
                case "NI":
                    myCommission = 0.5;
                    break;
                case "PB":
                    myCommission = 0.00002;
                    break;
                case "RB":
                    myCommission = 0.0001;
                    break;
                case "RU":
                    myCommission = 1.5;
                    break;
                case "SN":
                    myCommission = 0.5;
                    break;
                case "SP":
                    myCommission = 0.000025;
                    break;
                case "SS":
                    myCommission = 1.0;
                    break;
                case "WR":
                    myCommission = 0.00002;
                    break;
                case "ZN":
                    myCommission = 1.5;
                    break;
                //Stock
                case "0":
                case "3":
                case "6":
                    myCommission = 0.002;
                    break;
                default:
                    myCommission = 0.0;
                    break;
            }
            return myCommission;
        }
        public static double MyCost(Instrument instr, double qty, double price)
        {
            double myCost = 0.0;
            CommissionType myType = MyCommissionType(instr.Symbol);
            double myCommission = MyCommission(instr.Symbol);
            switch (myType)
            {
                case CommissionType.PerShare:
                    myCost = myCommission * qty;
                    break;
                case CommissionType.Percent:
                    myCost = myCommission * qty * price * (instr.Type == InstrumentType.Stock ? 1 : instr.Factor);
                    break;
                case CommissionType.Absolute:
                    myCost = myCommission;
                    break;
                default:
                    throw new NotSupportedException("!!!!!!!!!!!!!!!!!! Warning: Unknown commission type [" + myType + "]!");
            }
            return myCost;
        }
    }

    public class MyStrategyPosition
    {
        public int Amount = 0;
        public double AvgEntryPrice = 0.0;
        public double PtfVal0 = 0.0;    //Realized, Portfolio Value At Round Start
        public double PtfVal = 0.0;     //Realized, Portfolio Value On Fill
        public double PtfMaxVal = 0.0;  //Realized + Unrealized, Max Portfolio Value
        public double PosMaxPnL = 0.0;  //Realized + Unrealized, Max PnL Within A Round  
        public MyStrategyPosition()
        {
        }
        public void OnFill(Strategy strategy, Fill fill)
        {
            int lastAmount = Amount;
            double myCost = MyCommissionProvider.MyCost(fill.Instrument, fill.Qty, fill.Price);
            double myFactor = fill.Instrument.Type == InstrumentType.Stock ? 1 : fill.Instrument.Factor;
            if (fill.Side == OrderSide.Buy)
            {
                Amount += (int)fill.Qty;
            }
            else if (fill.Side == OrderSide.Sell)
            {
                Amount -= (int)fill.Qty;
            }
            if (lastAmount == 0)
            {
                PtfVal = PtfVal - myCost;
                AvgEntryPrice = fill.Price;
            }
            else if (Amount == 0)
            {
                PtfVal = PtfVal - myCost + (fill.Side == OrderSide.Buy ? -1 : 1) * fill.Qty * (fill.Price - AvgEntryPrice) * myFactor;
                PtfVal0 = PtfVal;
                PosMaxPnL = 0.0;
                AvgEntryPrice = 0.0;
            }
            else if (lastAmount > 0 && Amount > 0)
            {
                if (fill.Side == OrderSide.Buy)
                {
                    PtfVal = PtfVal - myCost;
                    AvgEntryPrice = (lastAmount * AvgEntryPrice + fill.Qty * fill.Price) / (lastAmount + fill.Qty);
                }
                else
                {
                    PtfVal = PtfVal - myCost + fill.Qty * (fill.Price - AvgEntryPrice) * myFactor;
                    AvgEntryPrice = (lastAmount * AvgEntryPrice - fill.Qty * fill.Price) / (lastAmount - fill.Qty);
                }
            }
            else if (lastAmount < 0 && Amount < 0)
            {
                if (fill.Side == OrderSide.Sell)
                {
                    PtfVal = PtfVal - myCost;
                    AvgEntryPrice = (Math.Abs(lastAmount) * AvgEntryPrice + fill.Qty * fill.Price) / (Math.Abs(lastAmount) + fill.Qty);
                }
                else
                {
                    PtfVal = PtfVal - myCost - fill.Qty * (fill.Price - AvgEntryPrice) * myFactor;
                    AvgEntryPrice = (Math.Abs(lastAmount) * AvgEntryPrice - fill.Qty * fill.Price) / (Math.Abs(lastAmount) - fill.Qty);
                }
            }
            Persistence.SaveStrategyPosition(strategy);
            MyAccountPosition.OnFill(fill);
        }
    }

    public class MyInstrumentPosition
    {
        public int TodayLong = 0;
        public int TodayShort = 0;
        public int YesterdayLong = 0;
        public int YesterdayShort = 0;
        public int FrozenTodayLong = 0;
        public int FrozenTodayShort = 0;
        public int FrozenYesterdayLong = 0;
        public int FrozenYesterdayShort = 0;
        public MyInstrumentPosition()
        {
        }
        public int Amount
        {
            get { return (TodayLong + YesterdayLong) - (TodayShort + YesterdayShort); }
        }
        public void OnFill(Fill fill)
        {
            if (fill.Order.Instrument.Type == InstrumentType.Future)
            {
                if (fill.Order.GetOpenClose() == QuantBox.XApi.OpenCloseType.Open && fill.Order.Side == OrderSide.Buy)
                {
                    TodayLong += (int)fill.Qty;
                }
                if (fill.Order.GetOpenClose() == QuantBox.XApi.OpenCloseType.Open && fill.Order.Side == OrderSide.Sell)
                {
                    TodayShort += (int)fill.Qty;
                }
                if (fill.Order.GetOpenClose() != QuantBox.XApi.OpenCloseType.Open && fill.Order.Side == OrderSide.Buy)
                {
                    if (fill.Order.Instrument.Exchange != "SHFE" && fill.Order.Instrument.Exchange != "INE")
                    {
                        YesterdayShort -= (int)fill.Qty;
                        FrozenYesterdayShort -= (int)fill.Qty;
                    }
                    else
                    {
                        if (fill.Order.GetOpenClose() == QuantBox.XApi.OpenCloseType.CloseToday)
                        {
                            TodayShort -= (int)fill.Qty;
                            FrozenTodayShort -= (int)fill.Qty;
                        }
                        else if (fill.Order.GetOpenClose() == QuantBox.XApi.OpenCloseType.Close)
                        {
                            YesterdayShort -= (int)fill.Qty;
                            FrozenYesterdayShort -= (int)fill.Qty;
                        }
                    }
                }
                if (fill.Order.GetOpenClose() != QuantBox.XApi.OpenCloseType.Open && fill.Order.Side == OrderSide.Sell)
                {
                    if (fill.Order.Instrument.Exchange != "SHFE" && fill.Order.Instrument.Exchange != "INE")
                    {
                        YesterdayLong -= (int)fill.Qty;
                        FrozenYesterdayLong -= (int)fill.Qty;
                    }
                    else
                    {
                        if (fill.Order.GetOpenClose() == QuantBox.XApi.OpenCloseType.CloseToday)
                        {
                            TodayLong -= (int)fill.Qty;
                            FrozenTodayLong -= (int)fill.Qty;
                        }
                        else if (fill.Order.GetOpenClose() == QuantBox.XApi.OpenCloseType.Close)
                        {
                            YesterdayLong -= (int)fill.Qty;
                            FrozenYesterdayLong -= (int)fill.Qty;
                        }
                    }
                }
            }
            else if (fill.Order.Instrument.Type == InstrumentType.Stock)
            {
                if (fill.Order.Side == OrderSide.Buy)
                    TodayLong += (int)fill.Qty;
                else if (fill.Order.Side == OrderSide.Sell)
                    YesterdayLong -= (int)fill.Qty;
            }
        }
        public void OnOrderReject(Order order)
        {
            OrderSide side = order.Side;
            int leavesQty = (int)order.LeavesQty;
            if (order.GetOpenClose() == QuantBox.XApi.OpenCloseType.Open) return;
            if (side == OrderSide.Buy)
            {
                if (order.Instrument.Exchange != "SHFE" && order.Instrument.Exchange != "INE")
                {
                    FrozenYesterdayShort -= leavesQty;
                }
                else
                {
                    if (order.GetOpenClose() == QuantBox.XApi.OpenCloseType.Close)
                    {
                        FrozenYesterdayShort -= leavesQty;
                    }
                    else if (order.GetOpenClose() == QuantBox.XApi.OpenCloseType.CloseToday)
                    {
                        FrozenTodayShort -= leavesQty;
                    }
                }

            }
            else if (side == OrderSide.Sell)
            {
                if (order.Instrument.Exchange != "SHFE" && order.Instrument.Exchange != "INE")
                {
                    FrozenYesterdayLong -= leavesQty;
                }
                else
                {
                    if (order.GetOpenClose() == QuantBox.XApi.OpenCloseType.Close)
                    {
                        FrozenYesterdayLong -= leavesQty;
                    }
                    else if (order.GetOpenClose() == QuantBox.XApi.OpenCloseType.CloseToday)
                    {
                        FrozenTodayLong -= leavesQty;
                    }
                }

            }
            return;
        }
        public void ChangeDay()
        {
            YesterdayLong += TodayLong;
            YesterdayShort += TodayShort;
            TodayLong = 0;
            TodayShort = 0;
            FrozenTodayLong = 0;
            FrozenTodayShort = 0;
            FrozenYesterdayLong = 0;
            FrozenYesterdayShort = 0;
        }
    }

    public class MyAccountPosition
    {
        public static Dictionary<string, MyInstrumentPosition> AccountPositions = new Dictionary<string, MyInstrumentPosition>();
        public static void AddInstrument(string symbol)
        {
            if (!AccountPositions.ContainsKey(symbol)) AccountPositions.Add(symbol, new MyInstrumentPosition());
        }
        public static MyInstrumentPosition GetInstrumentPosition(string symbol)
        {
            if (!AccountPositions.ContainsKey(symbol)) AddInstrument(symbol);
            return AccountPositions[symbol];
        }
        public static void OnFill(Fill fill)
        {
            if (!AccountPositions.ContainsKey(fill.Instrument.Symbol)) AddInstrument(fill.Instrument.Symbol);
            AccountPositions[fill.Instrument.Symbol].OnFill(fill);
            Persistence.SaveAccountPosition();
        }
        public static void OnOrderReject(Order order)
        {
            if (!AccountPositions.ContainsKey(order.Instrument.Symbol)) AddInstrument(order.Instrument.Symbol);
            AccountPositions[order.Instrument.Symbol].OnOrderReject(order);
            Persistence.SaveAccountPosition();
        }
    }

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)]
    public class ParamPersistenceAttribute : Attribute { }
    public class Persistence
    {
        private static JsonSerializerSettings jSetting = new JsonSerializerSettings()
        {
            Formatting = Formatting.Indented,
        };

        public static string APDirectory;
        private static string APFileName = "AccountPosition.json";
        private static bool CheckDirectoryFile(string directory, string filename)
        {
            bool bexist = true;
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
                bexist = false;
            }
            if (!File.Exists(directory + filename))
            {
                File.Create(directory + filename).Close();
                bexist = false;
            }
            return bexist;
        }
        public static void LoadAccountPosition()
        {
            CheckDirectoryFile(APDirectory, APFileName);
            try
            {
                using (TextReader reader = new StreamReader(APDirectory + APFileName))
                {
                    string strRead = reader.ReadToEnd();
                    reader.Close();
                    if (strRead == null) return;
                    MyAccountPosition.AccountPositions = JsonConvert.DeserializeObject(strRead, typeof(Dictionary<string, MyInstrumentPosition>)) as Dictionary<string, MyInstrumentPosition>;
                    if (MyAccountPosition.AccountPositions == null)
                    {
                        MyAccountPosition.AccountPositions = new Dictionary<string, MyInstrumentPosition>();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        public static void SaveAccountPosition()
        {
            CheckDirectoryFile(APDirectory, APFileName);
            string str = JsonConvert.SerializeObject(MyAccountPosition.AccountPositions, typeof(Dictionary<string, MyInstrumentPosition>), jSetting);
            using (TextWriter writer = new StreamWriter(APDirectory + APFileName, false))
            {
                writer.Write("{0}", str);
                writer.Close();
            }
        }
        public static void LoadStratgyPosition(Strategy strategy)
        {
            FieldInfo[] fields = strategy.GetType().GetFields();
            foreach (FieldInfo field in fields)
            {
                if (strategy.GetType().GetField(field.Name).FieldType == typeof(MyStrategyPosition))
                {
                    string SPDirectory = APDirectory + strategy.Name + @"\";
                    string SPFileName = field.Name + ".json";
                    bool bexist = CheckDirectoryFile(SPDirectory, SPFileName);
                    if (bexist == false)
                    {
                        SaveStrategyPosition(strategy);
                    }
                    try
                    {
                        using (TextReader reader = new StreamReader(SPDirectory + SPFileName))
                        {
                            string strRead = reader.ReadToEnd();
                            if (strRead.Equals(""))
                            {
                                reader.Close();
                                continue;
                            }
                            var temp = JsonConvert.DeserializeObject(strRead, strategy.GetType().GetField(field.Name).FieldType);
                            strategy.GetType().GetField(field.Name).SetValue(strategy, temp);
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.ToString());
                    }
                }
            }
        }
        public static void SaveStrategyPosition(Strategy strategy)
        {
            FieldInfo[] fields = strategy.GetType().GetFields();
            foreach (FieldInfo field in fields)
            {
                if (strategy.GetType().GetField(field.Name).FieldType == typeof(MyStrategyPosition))
                {
                    string SPDirectory = APDirectory + strategy.Name + @"\";
                    string SPFileName = field.Name + ".json";
                    bool bexist = CheckDirectoryFile(SPDirectory, SPFileName);
                    try
                    {
                        string str = JsonConvert.SerializeObject(strategy.GetType().GetField(field.Name).GetValue(strategy), strategy.GetType().GetField(field.Name).FieldType, jSetting);
                        using (TextWriter writer = new StreamWriter(SPDirectory + SPFileName, false))
                        {
                            writer.Write("{0}", str);
                            writer.Close();
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.ToString());
                    }
                }
            }
        }
    }
}
